using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using Data.Merge;
using TreeView.Graphic;
using Data.DataTranslator;
using Logic.Agglomerative;

namespace TreeView.Logic
{
    public class RadialTree
    {
        const int INTERNAL_NODE_SISE = 5;
        const int LEAF_NODE_SISE = 5;
        private ArrayList originalMergeList;
        private ArrayList treeNodes;
        private double teta;
        //private ArrayList genomeNames;
        private RadialTreeNode root;

        #region Constructors

        /// <summary>
        /// 
        /// </summary>
        /// <param name="newTreeNodes">array list of RadialTreeNodes</param>
        /// <param name="newRoot">the node wich is the root of the tree</param>
        /// <param name="GenomeNames">array list of the names of the genomes to convert the key to string</param>
        //public RadialTree(ArrayList newTreeNodes)
        //{
        //    treeNodes = newTreeNodes;
        //    RadTree2MergList();
        //}

        /// <summary>
        /// 
        /// </summary>
        /// <param name="mergeList"></param>
        /// <param name="maxTeta"></param>
        /// <param name="GenomeNames"></param>
        public RadialTree(ArrayList mergeList, double maxTeta)
        {
            treeNodes = new ArrayList();
            originalMergeList = mergeList;
            MergList2RadTree(mergeList, maxTeta);
            teta = maxTeta;
        }

        #endregion

        #region Properties

        public double Teta
        {
            get { return teta; }
            set { teta = value; }
        }


        public ArrayList TreeNodes
        {
            get { return treeNodes; }
        }

        public RadialTreeNode Root
        {
            get { return root; }
        }

        #endregion

        #region Methods

        /// <summary>
        /// this method go over the tree and reconstruct the merge list 
        /// as it was before the creation of the radial tree
        /// </summary>
        /// <param name="RadTree"></param>
        public ArrayList RadTree2MergList()
        {
            //ArrayList newMergList = new ArrayList();
            //MergeStep newMergStep;
            //foreach (RadialTreeNode node in treeNodes)
            //{
            //    if(!node.isLeaf())
            //        newMergStep = new MergeStep(node.Key,node.LeftSon.Key,node.RightSon.Key,node.Hight);
            //    else
            //        newMergStep = new MergeStep(node.Key, -1, -1, 0);
            //    newMergList.Add(newMergStep);
            //}
            return originalMergeList;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="mergelist"></param>
        /// <param name="maxAngle">between 0.25 to 2 means the part of the circle that 
        /// the tree will spread on</param>
        private void MergList2RadTree(ArrayList mergelist, double maxAngle)
        {
            int LeafNumber = 0;
            foreach (MergeStep node in mergelist)
            {
                if (node.isLeaf())
                {
                    LeafNumber++;
                }
            }
            int LeafPosition = 0;
            foreach (MergeStep node in mergelist)
            {
                if (node.isLeaf())
                {
                    double teta;
                    if (maxAngle < 2)
                        teta = LeafPosition * maxAngle * Math.PI / (LeafNumber - 1);
                    else
                        teta = LeafPosition * 2 * Math.PI / LeafNumber;
                    RadialTreeNode newRadNode = new RadialTreeNode(teta, node.Name, node.Depth, node.Hight, kDimMap.getGenomeName(node.Name),node.ClusterNum, LEAF_NODE_SISE);
                    treeNodes.Add(newRadNode);
                    LeafPosition++;
                }
                else
                {
                    RadialTreeNode leftChild = null;
                    RadialTreeNode rightChild = null;
                    foreach (RadialTreeNode c in treeNodes)
                    {
                        if (c.Key == node.Leftson)
                            leftChild = c;
                        if (c.Key == node.Rightson)
                            rightChild = c;
                    }
                    if ((leftChild != null) && (rightChild != null))
                        treeNodes.Add(CreateMutualFather(leftChild, rightChild, node.Name));
                }
            }
            root = (RadialTreeNode)treeNodes[treeNodes.Count - 1];
        }

        /// <summary>
        /// this function get a new root of type RadialTreeNode and creat it's sub tree in
        /// the radial tree format.
        /// </summary>
        /// <param name="newRoot">the root of the sub tree</param>
        /// <param name="MaxTeta">the maximal angle the sub tree will be spread on</param>
        /// <returns></returns>
        public RadialTree SubRadialTree(RadialTreeNode newRoot, double MaxTeta)
        {
            int mostLeftChildKey;
            int rootKey;
            RadialTreeNode node = newRoot;
            try
            {
                if (node != null)
                {
                    while ((node.isLeaf() != true) && (node.LeftSon != null))
                        node = node.LeftSon;
                    mostLeftChildKey = node.Key;
                    rootKey = newRoot.Key;
                    ArrayList newMergList = new ArrayList();
                    int CopyFlag = 0;
                    foreach (MergeStep Merge in originalMergeList)
                    {
                        if (Merge.Name == mostLeftChildKey)
                            CopyFlag = 1;
                        if (Merge.Name == rootKey)
                        {
                            MergeStep newStep = new MergeStep(Merge.Name, Merge.Leftson, Merge.Rightson, Merge.Hight);
                            newStep.Depth = Merge.Depth - newRoot.Depth;
                            newMergList.Add(newStep);
                            CopyFlag = 0;
                        }
                        if (CopyFlag == 1)
                        {
                            MergeStep newStep = new MergeStep(Merge.Name, Merge.Leftson, Merge.Rightson, Merge.Hight);
                            newStep.Depth = Merge.Depth - newRoot.Depth;
                            newMergList.Add(newStep);
                        }
                    }
                    return new RadialTree(newMergList, MaxTeta);
                }
                else
                {
                    return new RadialTree(originalMergeList, MaxTeta);
                }
                
            }
            catch (NullReferenceException)
            {
                MessageBox.Show("The new root does not exisist", "Error!!!");
                return this;
            }
        }

        /// <summary>
        /// This function get two nodes and creat thier mutual father in the tree
        /// it calculate the father angle and radius
        /// </summary>
        /// <param name="LeftSon">The left son node</param>
        /// <param name="RightSon">The right son node</param>
        /// <param name="key">The father new key</param>
        /// <returns>Returns a radialTreeNode with all the father data</returns>
        private RadialTreeNode CreateMutualFather(RadialTreeNode LeftSon, RadialTreeNode RightSon, int key)
        {
            RadialTreeNode Father = new RadialTreeNode((LeftSon.Teta + RightSon.Teta) / 2, key, RightSon.Depth - 1, Math.Max(RightSon.Hight, LeftSon.Hight) + 1, INTERNAL_NODE_SISE,LeftSon.ClusterNum, Color.Black);
            Father.LeftSon = LeftSon;
            Father.RightSon = RightSon;
            Father.SonsCodeNames.AddRange(LeftSon.SonsCodeNames);
            Father.SonsCodeNames.AddRange(RightSon.SonsCodeNames);
            return Father;
        }
        
        /// <summary>
        /// This method go over the tree and count the number of leafs in it.
        /// </summary>
        /// <param name="root">The root of the tree</param>
        /// <returns>The leafs number</returns>
        private int countLeafs(RadialTreeNode root)
        {
            int leafsNumber = 0;
            if (root == null)
                return 0;
            if (root.isLeaf() == true)
                return leafsNumber + 1;
            else
            {
                leafsNumber = countLeafs(root.LeftSon) + countLeafs(root.RightSon);
                return leafsNumber;
            }
        }

        /// <summary>
        /// This methos creat the radialTreeGui objects which help us to draw the radial tree
        /// </summary>
        /// <param name="root">the root of the tree we want to print</param>
        /// <param name="Width">Width of the Panel we want to print on</param>
        /// <param name="Height">Height of the Panel we want to print on</param>
        public void createGUIRadTree(ArrayList treeNodes,int Width,int Height,int property)
        {
            int leafCount = 0;
            int numOfClusters = 0;
            int H = 0;

            foreach (RadialTreeNode node in treeNodes)
            {
                if (node.isLeaf())
                    leafCount++;
                numOfClusters = Math.Max(numOfClusters, node.ClusterNum);
            }

            foreach (RadialTreeNode node in treeNodes)
            {
                if (!node.isLeaf())
                {
                    Color fatherColor = Color.FromArgb((int)((node.LeftSon.NodeGUI.BrunchColor.R + node.RightSon.NodeGUI.BrunchColor.R) / 2), (int)((node.LeftSon.NodeGUI.BrunchColor.G + node.RightSon.NodeGUI.BrunchColor.G) / 2), (int)((node.LeftSon.NodeGUI.BrunchColor.B + node.RightSon.NodeGUI.BrunchColor.B) / 2));
                    Brush fatherBrush = new SolidBrush(fatherColor);
                    node.NodeGUI = new RadNodeGUI(node, INTERNAL_NODE_SISE, fatherColor, fatherBrush, fatherBrush, "No Name", Width, Height);
                    node.NodeGUI.LeftChild = node.LeftSon.NodeGUI;
                    node.NodeGUI.RightChild = node.RightSon.NodeGUI;
                }
                else
                {
                    //change to the prop code
                    //switch (property)
                    //{
                    //    //this is reset Property coloring by clusters colors;
                    //    case -1:
                    //        H = (int)((double)(((float)((float)(node.ClusterNum) / ((float)numOfClusters + 1))) * 240));
                    //        break;
                    //    case 1:
                    //        H = (int)((double)(((float)((float)(node.Key) / (float)treeNodes.Count))) * 240);
                    //        break;
                    //    case 2:
                    //        H = (int)((double)(((float)((float)(node.Key) / (float)treeNodes.Count))) * 240);
                    //        break;
                    //}
                    switch (property)
                    {
                        //this is reset Property coloring by clusters colors;
                        case -1:
                            H = (int)(((double)(node.ClusterNum) / ((double)numOfClusters + 1)) * 240);
                            break;
                        case 1:
                            H = (int)(((double)(kDimMap.getPropertyCode(node.Key,0)) / (double)kDimMap.getNumberOfPropertiesInCategory(0)) * 240);
                            break;
                        case 2:
                            H = (int)(((double)(kDimMap.getPropertyCode(node.Key, 1)) / (double)kDimMap.getNumberOfPropertiesInCategory(1)) * 240);
                            break;
                    }
                    Color temp = kDimMap.HLStoRGB(H, 120, 240);
                    Brush myBrush = new SolidBrush(temp);
                    
                    node.NodeGUI = new RadNodeGUI(node, LEAF_NODE_SISE, temp, myBrush, myBrush, node.Name, Width, Height);
                    node.NodeGUI.BrunchColor = temp;
                }
            }
        }

        public void createGUIRadTree(ArrayList treeNodes, int Width, int Height)
        {
            createGUIRadTree(treeNodes, Width, Height, -1);
        }
        #endregion
    }
}
